#ifndef __FOCUS_H__
#define __FOCUS_H__


#include "base.pb.h"
#include "motor_controller.h"
#include "cam_tele.h"
#include "cam_wide.h"
#include "ws_constant.h"

#include "opencv2/opencv.hpp"

#include <atomic>
#include <tuple>
#include <string>

struct templateRect {
    int left_top_w;
    int left_top_h;
    int width;
    int height;
};

struct HfaData {
    int hfa_index;
    float hfa_value;

    HfaData(int index, float value) : hfa_index(index), hfa_value(value) {}
    bool operator<(const HfaData& other) const {
        return hfa_value < other.hfa_value;
    }
    bool operator>(const HfaData& other) const {
        return hfa_value > other.hfa_value;
    }
};

class ITips;

class Focus {
public:
    Focus();
    ~Focus();

    int handleMessage(WsPacket& ws_packet);
    int autoExposure();
    int autoFocus(int mode, int center_x, int center_y);
    int autoFoucusClimbing(int direction, int time_out, int mode, int centerX, int centerY);
    int manualSingleStepFocus(MotorFocusDirection direction);
    int startManualContinuFocus(MotorFocusDirection direction);
    int stopManualContinuFocus();
    int getRoiaSharpness(unsigned long long& value);
    int getWindowSharpness(int center_x, int center_y, unsigned long long& value);
    int checkSunMoonRect();
    int checkWideSunMoon();

    int getInfinityFocusPoint(float& infinity_focus_point_from_file);

    int autoFocus(WsPacket& ws_packet);
    int manualSingleStepFocus(WsPacket& ws_packet);
    int startManualContinuFocus(WsPacket& ws_packet);
    int stopManualContinuFocus(WsPacket& ws_packet);
    int startAstroAutoFocus(WsPacket& ws_packet);
    int startAstroAutoFocusNew(WsPacket& ws_packet);
    int startAstroAutoFocusNew(int mode);
    int stopAstroAutoFocusNew(WsPacket& ws_packet);
    int getAstroTestData(WsPacket& ws_packet);
    templateRect getCurTemplateRect();
    templateRect calcCurTemplateRect();

    void notifyFocusState();
    inline StateNotify isFocusStateIdle() { 
        if (astro_fast_focus_state_ != STATE_IDLE || astro_slow_focus_state_ != STATE_IDLE || normal_focus_state_ != STATE_IDLE || area_focus_state_ != STATE_IDLE)
            return STATE_RUNNING;
        return STATE_IDLE;
    }
    inline int getFocusState(int function_id) {
        if (function_id == CAM_FUNCTION_ASTRO_AUTO_FOCUS)
            return astro_slow_focus_state_;
        else if (function_id == CAM_FUNCTION_ASTRO_AUTO_FOCUS_FAST)
            return astro_fast_focus_state_;
        else if (function_id == CAM_FUNCTION_NORMAL_AUTO_FOCUS)
            return normal_focus_state_;
        else if (function_id == CAM_FUNCTION_AREA_AUTO_FOCUS)
            return area_focus_state_;
    }
    inline bool HaveTryAstroAutoFocus() { return b_try_astro_auto_focus_; }

    inline void addITipsObserver(ITips* itips_observer) { itips_observer_ = itips_observer; }

private:
    std::tuple<cv::Rect, double> matchTemplate();
    int getHfd(const cv::Mat& img_src, float& hfd);

    void setAndNotifyFocusState(const StateNotify& state, int function_id);

private:
    static std::atomic<StateNotify> astro_fast_focus_state_;
    static std::atomic<StateNotify> astro_slow_focus_state_;
    static std::atomic<StateNotify> normal_focus_state_;
    static std::atomic<StateNotify> area_focus_state_;

    static ITips* itips_observer_;

    static bool b_try_astro_auto_focus_;

public:
    CamTele cam_tele_;
    CamWide cam_wide_;
};



#endif